/*
Copyright Paul James Mutton, 2001-2003, http://www.jibble.org/

This file is part of EpsGraphics2D.

This software is free for personal, educational and non-commercial
use only. A license is available for commercial use. An unlimited
license is also available, allowing you to distribute this software
as part of your own applications and to modify the source code as
you see fit.

This source code, or parts thereof, may not be distributed in any
modified form without permission from the author.

EpsGraphics2D is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

$Author: pjm2 $
$Id: EpsDocument.java,v 1.5 2003/11/29 17:14:18 pjm2 Exp $
*/

package org.jibble.epsgraphics;

import java.util.*;
import java.io.*;


/**
 * This represents an EPS document. Several EpsGraphics2D objects may point
 * to the same EpsDocument.
 *  <p>
 * Copyright Paul Mutton,
 *           <a href="http://www.jibble.org/">http://www.jibble.org/</a>
 * 
 * @author  $Author: pjm2 $
 * @version $Id: EpsDocument.java,v 1.5 2003/11/29 17:14:18 pjm2 Exp $
 */
public class EpsDocument {
    
    
    /**
     * Constructs an empty EpsDevice.
     */
    public EpsDocument(String title) {
        _headers = new StringBuffer();
        _contents = new StringBuffer();
        _title = title;
        minX = Float.POSITIVE_INFINITY;
        minY = Float.POSITIVE_INFINITY;
        maxX = Float.NEGATIVE_INFINITY;
        maxY = Float.NEGATIVE_INFINITY;
    }
    
    /**
     * Constructs an empty EpsDevice that writes directly to a file.
     * Bounds must be set before use.
     */
    public EpsDocument(String title, File file, int minX, int minY, int maxX, int maxY) throws IOException {
        this(title);
        this.minX = minX;
        this.minY = minY;
        this.maxX = maxX;
        this.maxY = maxY;
        _writer = new BufferedWriter(new FileWriter(file));
        _file = file;
        write(_writer);
    }
    
    
    /**
     * Returns the title of the EPS document.
     */
    public synchronized String getTitle() {
        return _title;
    }
    
    
    /**
     * Updates the bounds of the current EPS document.
     */
    public synchronized void updateBounds(double x, double y) {
        if (x > maxX) {
            maxX = (float) x;
        }
        if (x < minX) {
            minX = (float) x;
        }
        if (y > maxY) {
            maxY = (float) y;
        }
        if (y < minY) {
            minY = (float) y;
        }
    }
    
    
    /**
     * Appends a line to the EpsDocument.  A new line character is added
     * to the end of the line when it is added.
     */
    public synchronized void append(EpsGraphics2D g, String line) {
        if (_lastG == null) {
            _lastG = g;
        }
        else if (g != _lastG) {
            EpsGraphics2D lastG = _lastG;
            _lastG = g;
            // We are being drawn on with a different EpsGraphics2D context.
            // We may need to update the clip, etc from this new context.
            if (g.getClip() != lastG.getClip()) {
                g.setClip(g.getClip());
            }
            if (!g.getColor().equals(lastG.getColor())) {
                g.setColor(g.getColor());
            }
            if (!g.getBackground().equals(lastG.getBackground())) {
                g.setBackground(g.getBackground());
            }
            // We don't need this, as this only affects the stroke and font,
            // which are dealt with separately later on.
            //if (!g.getTransform().equals(lastG.getTransform())) {
            //    g.setTransform(g.getTransform());
            //}
            if (!g.getPaint().equals(lastG.getPaint())) {
                g.setPaint(g.getPaint());
            }
            if (!g.getComposite().equals(lastG.getComposite())) {
                g.setComposite(g.getComposite());
            }
            if (!g.getComposite().equals(lastG.getComposite())) {
                g.setComposite(g.getComposite());
            }
            if (!g.getFont().equals(lastG.getFont())) {
                g.setFont(g.getFont());
            }
            if (!g.getStroke().equals(lastG.getStroke())) {
                g.setStroke(g.getStroke());
            }
        }
        _lastG = g;
        
        if (_file == null) {
            _contents.append(line + "\n");
        }
        else {
            try {
                _writer.write(line + "\n");
            }
            catch (IOException e) {
                throw new EpsException("Could not write to the output file: " + e);
            }
        }
    }
    
    
    /**
     * Outputs the contents of the EPS document to the specified
     * Writer, complete with headers and bounding box.
     */
    public synchronized void write(Writer writer) throws IOException {
        float offsetX = -minX;
        float offsetY = -minY;
        
        writer.write("%!PS-Adobe-3.0 EPSF-3.0\n");
        writer.write("%%Creator: EpsGraphics2D " + EpsGraphics2D.VERSION + " by Paul Mutton, http://www.jibble.org/\n");
        writer.write("%%Title: " + _title + "\n");
        writer.write("%%CreationDate: " + new Date() + "\n");
        writer.write("%%BoundingBox: 0 0 " + ((int) Math.ceil(maxX + offsetX)) + " " + ((int) Math.ceil(maxY + offsetY)) + "\n");
        writer.write("%%DocumentData: Clean7Bit\n");
        writer.write("%%DocumentProcessColors: Black\n");
        writer.write("%%ColorUsage: Color\n");
        writer.write("%%Origin: 0 0\n");
        writer.write("%%Pages: 1\n");
        writer.write("%%Page: 1 1\n");
        writer.write("%%EndComments\n\n");
        
        for (int i = 0; i < _headers.length(); i++) {
            writer.write(_headers.charAt(i));
        }
        writer.write("gsave\n");
        
        if (_file == null) {
            writer.write(offsetX + " " + (offsetY) + " translate\n");
        }
        else {
            writer.write(offsetX + " " + ((maxY - minY) - offsetY) + " translate\n");
        }
        
        
        for (int i = 0; i < _contents.length(); i++) {
            writer.write(_contents.charAt(i));
        }
        
        // Only write the footer if we are not passing to a file.
        if (_file == null) {
            writeFooter(writer);
        }
    }
    
    
    private void writeFooter(Writer writer) throws IOException {
        writer.write("grestore\n");
        if (isClipSet()) {
            writer.write("grestore\n");
        }
        writer.write("\n");
        writer.write("%%EOF");
        writer.flush();
    }
    
    
    public synchronized void close() throws IOException {
        if (_file != null) {
            writeFooter(_writer);
            _writer.flush();
            _writer.close();
        }
    }
    
    
    public boolean isClipSet() {
        return _isClipSet;
    }
    
    public void setClipSet(boolean isClipSet) {
        _isClipSet = isClipSet;
    }
    
    
    private float minX;
    private float minY;
    private float maxX;
    private float maxY;
    
    private boolean _isClipSet = false;

    private String _title;
    private StringBuffer _headers;
    private StringBuffer _contents;
    private File _file = null;
    private BufferedWriter _writer = null;
    
    // We need to remember which was the last EpsGraphics2D object to use
    // us, as we need to replace the clipping region if another EpsGraphics2D
    // object tries to use us.
    private EpsGraphics2D _lastG = null;
    
}